<!DOCTYPE html>
<html>

<head>
    <title>数据绑定学习</title>
</head>

<body>
    <div id="name"></div>
    <input type="text" id="miput" data-bind="b">
    <input type="text" id="miput2" data-bind="b">
    <div data-bind='b'>34</div>

    <script>
        //全局工具类
        class Tools {
            //对象继承
            static extend(_old, _new) {
                _old = _old || {};
                _new = _new || {};
                for (let key in _new) {
                    _old[key] = _new[key];
                }
                return _old;
            }
            //获取dom元素子标签
            static getSonNodeList(dom, nodes, filter, handler) {
                if (!nodes) {
                    nodes = [];
                } else {
                    if (filter && (typeof filter == 'function') && filter(dom)) {
                        if (handler && (typeof handler == 'function')) {
                            nodes.push(handler(dom));
                        } else {
                            nodes.push(dom);
                        }
                    } else {
                        nodes.push(dom);
                    }
                }
                let sonNodes = dom.children;
                if (sonNodes != null && sonNodes.length > 0) {
                    for (let i = 0; i < sonNodes.length; i++) {
                        this.getSonNodeList(sonNodes[i], nodes);
                    }
                }
                return nodes;
            }
        }

        //代理(拦截器)工厂 分两种proxy 和 Object.defineProperty
        class ProxyFactory {

            static createVarProxyByProxy(obj, thiz) {
                let proxy = new Proxy(obj, {
                    get: function (target, key, receiver) {
                        if (thiz.getfn) {
                            thiz.getfn(target, key, receiver);
                        }
                        return Reflect.get(target, key, receiver);
                    },
                    set: function (target, key, value, receiver) {
                        if (thiz.setfn) {
                            thiz.setfn(target, key, value, receiver);
                        }
                        return Reflect.set(target, key, value, receiver);
                    },
                });
                return proxy;
            }
        }


        class DataBind {

            constructor(opstion) {

                //默认参数
                this.default = {};

                //参数覆盖
                this.default = Tools.extend(this.default, opstion);

                //将要用于绑定的变量
                this.bindVars = opstion["bindVars"];

                this.proxyVars = null;

                //dom被订阅者 dom被订阅者 : 变量订阅者是 1:1
                this.domSubjects = [];

                //变量被订阅者  变量被订阅者 : dom订阅者 1:n
                this.varSubjects = {};

                this.init(opstion["dom"]);
            }

            init(dom) {
                this.proxyVars = ProxyFactory.createVarProxyByProxy(this.default["bindVars"], this);

                let sonNodeList = dom ? Tools.getSonNodeList(dom) : Tools.getSonNodeList(document.body);
                if (sonNodeList != null) {
                    sonNodeList.forEach((dom, index, arr) => {
                        let field = dom.getAttribute("data-bind");
                        if (field == null || field.replace(/\s/g, "").length < 1) {
                            return;
                        }
                        field = field.replace(/\s/g, "");
                        if (this.proxyVars.hasOwnProperty(field)) {
                            //添加var被订阅者
                            this.addVarSubject(field, dom);

                            //建立(dom被订阅者)
                            this.addDomSubject(field, dom);
                        }
                    });
                }

                //激活初始
                //for (let key in this.proxyVars) {
                //    this.bindVars[key] = this.bindVars[key];
                //}

            }

            getfn(target, key, receiver) {
                console.log(key + "被取值" + Reflect.get(target, key, receiver))
            }

            setfn(target, key, value) {
                console.log(key + "设置值" + value)
                this.varSubjects[key].notify(value);
            }

            //添加var被订阅者
            addVarSubject(field, dom) {
                //建立(dom订阅者)
                let domObs = new DomObserver(dom);
                //判断是否已经存在此(dom订阅者)对应的(变量被订阅者),没有就添加
                if (!this.varSubjects.hasOwnProperty(field)) {
                    this.varSubjects[field] = new VarSubject(this.proxyVars, field, this.bindVars[field]);
                }
                this.varSubjects[field].addObserver(domObs);
            }

            //添加Dom被订阅者
            addDomSubject(field, dom) {
                //建立(变量订阅者)
                let varObs = new VarObserver(this.proxyVars, field);
                //建立(dom被订阅者)
                let domSub = new DomSubject(dom);
                domSub.addObserver(varObs);
                this.domSubjects.push(domSub);
            }

        }


        //变量类型的被订阅者
        class VarSubject {
            constructor(obj, field, val) {
                this.obj = obj;
                this.field = field;
                this.val = val;
                this.observerArr = [];
            }

            //变量被获取时候执行的方法
            getfn() {
                console.log(this.field + "被读取了" + this.val);
            }

            //变量被修改时执行的方法
            setfn(newVal) {
                console.log(this.field + "被写入了:" + this.val);
                this.notify(newVal);
            }

            //添加订阅者
            addObserver(observer) {
                this.observerArr.push(observer);
            }
            //通知订阅者
            notify(newval) {
                this.observerArr.forEach(function (obs) {
                    obs.update(newval);
                })
            }
        }

        //dom类型的被订阅者
        class DomSubject {
            constructor(dom) {
                this.dom = dom;
                this.observerArr = [];
                let thiz = this;
                if (dom.nodeName == 'INPUT') {
                    if (dom.type == 'text') {
                        dom.oninput = function () {
                            thiz.notify(dom.value);
                        };
                    } else {
                        console.log(dom.nodeName + "-" + dom.type + "暂时未添加监听变更")
                    }
                } else {
                    console.log(dom.nodeName + "暂时不支持监听变更")
                }
            }
            //添加订阅者
            addObserver(observer) {
                this.observerArr.push(observer);
            }
            //通知订阅者
            notify(newval) {
                this.observerArr.forEach(function (obs) {
                    obs.update(newval);
                })
            }
        }

        //变量订阅者
        class VarObserver {
            constructor(obj, field) {
                this.obj = obj;
                this.field = field;
            }
            update(newVal) {
                this.obj[this.field] = newVal;
            }
        }

        //dom订阅者
        class DomObserver {
            constructor(dom) {
                this.dom = dom;
            }
            update(newVal) {
                let val = newVal;
                if (typeof newVal === "function") {
                    val = newVal();
                }
                if (this.dom.nodeName == 'INPUT') {
                    if (this.dom.type == 'text') {
                        this.dom.value = val;
                    }
                    if (this.dom.type == 'checkbox') {
                        console.log("checkbox暂不支持");
                    }
                } else {
                    if ('innerHTML' in this.dom) {
                        this.dom.innerHTML = val;
                    } else {
                        console.log(this.dom.nodeName + "暂不支持变更");
                    }
                }
            }
        }




        var ops = {
            bindVars: {

                b: "谁是谁"
            }
        };
        var page = new DataBind(ops);

    </script>
</body>

</html>